# ToBeanRowTransformer.java

    package org.codefilarete.stalactite.mapping;

    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.function.Function;

    import org.codefilarete.stalactite.sql.result.ColumnedRow;
    import org.codefilarete.tool.Reflections;
    import org.codefilarete.tool.bean.FieldIterator;
    import org.codefilarete.tool.collection.Iterables;
    import org.codefilarete.reflection.Accessors;
    import org.codefilarete.reflection.Mutator;
    import org.codefilarete.stalactite.sql.ddl.structure.Column;
    import org.codefilarete.stalactite.sql.ddl.structure.Table;

    /**
     * Class for transforming columns into a bean.
     *
     * @author Guillaume Mary
     */
    public class ToBeanRowTransformer<C> extends AbstractTransformer<C> {

    	private final Map<Column<?, ?>, Mutator<C, Object>> columnToMember;

    	/**
    	 * A constructor that maps all fields of a class by name
    	 *
    	 * @param clazz instance type to be built
    	 * @param table the mapped table
    	 * @param warnOnMissingColumn indicates if an Exception must be thrown when no {@link Column} in the {@link Table} can be found for a field
    	 * (matching name)
    	 */
    	public ToBeanRowTransformer(Class<C> clazz, Table table, boolean warnOnMissingColumn) {
    		this(clazz, new HashMap<>(10));
    		Map<String, Column> columnPerName = table.mapColumnsOnName();
    		FieldIterator fieldIterator = new FieldIterator(clazz);
    		Iterables.stream(fieldIterator).forEach(field -> {
    					Column column = columnPerName.get(field.getName());
    					if (column == null) {
    						if (warnOnMissingColumn) {
    							throw new UnsupportedOperationException("Missing column for field " + Reflections.toString(field));
    						}
    					} else {
    						columnToMember.put(column, Accessors.mutatorByField(field));
    					}
    				}
    		);
    	}

    	/**
    	 *
    	 * @param clazz the constructor to be used to instantiate a new bean
    	 * @param columnToMember the mapping between key in rows and helper that fixes values of the bean
    	 */
    	public ToBeanRowTransformer(Class<C> clazz, Map<? extends Column<?, ?>, ? extends Mutator<C, Object>> columnToMember) {
    		super(clazz);
    		this.columnToMember = (Map<Column<?, ?>, Mutator<C, Object>>) columnToMember;
    	}

    	/**
    	 *
    	 * @param beanFactory factory to be used to instantiate a new bean
    	 * @param columnToMember the mapping between key in rows and helper that fixes values of the bean
    	 */
    	public ToBeanRowTransformer(Function<? extends ColumnedRow, C> beanFactory,
    								Map<? extends Column<?, ?>, ? extends Mutator<C, Object>> columnToMember) {
    		super(beanFactory);
    		this.columnToMember = (Map<Column<?, ?>, Mutator<C, Object>>) columnToMember;
    	}

    	protected ToBeanRowTransformer(Function<ColumnedRow, C> beanFactory,
    								   Map<? extends Column<?, ?>, ? extends Mutator<C, Object>> columnToMember,
    								   Collection<TransformerListener<C>> rowTransformerListeners) {
    		super(beanFactory, rowTransformerListeners);
    		this.columnToMember = (Map<Column<?, ?>, Mutator<C, Object>>) columnToMember;
    	}

    	public Map<Column<?, ?>, Mutator<C, Object>> getColumnToMember() {
    		return columnToMember;
    	}

    	@Override
    	public void applyRowToBean(ColumnedRow source, C targetRowBean) {
    		for (Entry<Column<?, ?>, Mutator<C, Object>> columnFieldEntry : columnToMember.entrySet()) {
    			Object propertyValue = source.get(columnFieldEntry.getKey());
    			applyValueToBean(targetRowBean, columnFieldEntry, propertyValue);
    		}
    	}

    	protected void applyValueToBean(C targetRowBean, Entry<? extends Column, ? extends Mutator<C, Object>> columnFieldEntry, Object propertyValue) {
    		columnFieldEntry.getValue().set(targetRowBean, propertyValue);
    	}
    }